/*****************************************************************************
*  Copyright Statement:
*  --------------------
*  This software is protected by Copyright and the information contained
*  herein is confidential. The software may not be copied and the information
*  contained herein may not be used or disclosed except with the written
*  permission of MediaTek Inc. (C) 2006
*
*  BY OPENING THIS FILE, BUYER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
*  THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
*  RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO BUYER ON
*  AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
*  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
*  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
*  NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
*  SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
*  SUPPLIED WITH THE MEDIATEK SOFTWARE, AND BUYER AGREES TO LOOK ONLY TO SUCH
*  THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. MEDIATEK SHALL ALSO
*  NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE RELEASES MADE TO BUYER'S
*  SPECIFICATION OR TO CONFORM TO A PARTICULAR STANDARD OR OPEN FORUM.
*
*  BUYER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND CUMULATIVE
*  LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
*  AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
*  OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY BUYER TO
*  MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
*
*  THE TRANSACTION CONTEMPLATED HEREUNDER SHALL BE CONSTRUED IN ACCORDANCE
*  WITH THE LAWS OF THE STATE OF CALIFORNIA, USA, EXCLUDING ITS CONFLICT OF
*  LAWS PRINCIPLES.  ANY DISPUTES, CONTROVERSIES OR CLAIMS ARISING THEREOF AND
*  RELATED THERETO SHALL BE SETTLED BY ARBITRATION IN SAN FRANCISCO, CA, UNDER
*  THE RULES OF THE INTERNATIONAL CHAMBER OF COMMERCE (ICC).
*
*****************************************************************************/

/*******************************************************************************
 * Filename:
 * ---------
 * drm_memory.c
 *
 * Project:
 * --------
 *   MAUI
 *
 * Description:
 * ------------
 *
 *  Contains OS Abstraction Interface function declarations for
 *  allocating, reallocating and freeing memory. Normally the
 *  implementation of these are \#define-s to CRT functions malloc(),
 *  realloc() and free().
 *
 * Author:
 * -------
 * -------
 * -------
 *
 *==============================================================================
 *             HISTORY
 * Below this line, this part is controlled by PVCS VM. DO NOT MODIFY!!
 *------------------------------------------------------------------------------
 * removed!
 *
 * removed!
 * removed!
 * removed!
 *
 * removed!
 * removed!
 * removed!
 *
 * removed!
 * removed!
 * removed!
 *
 * removed!
 * removed!
 * removed!
 *******************************************************************************/
/*
 * Include
 */
#ifdef __DRM_SUPPORT__
#ifdef __DRM_V02__

#include "kal_release.h"        /* Basic data type */
#include "stack_common.h"
#include "drm_memory.h"
#include "drm_util.h"

#if !defined(__PRODUCTION_RELEASE__) //&& !defined(__MTK_TARGET__)
#define DRM_MEM_DEBUG_ON
#endif

#ifdef DRM_MEM_DEBUG_ON
#define DRM_MEM_LOG_ON
//#define DRM_MEM_CHECK_ON
#define DRM_MEM_DEBUG(x) //drm_trace x
#else
#define DRM_MEM_DEBUG(x)
#endif

#define DRM_MEM_SIZE (150 * 1024)

/* Use kal_uint32 to be 4-byte aligned */
static kal_uint32 drmt_mem_pool[DRM_MEM_SIZE / 4] = {0};
static KAL_ADM_ID drmt_mem_id = NULL;
static kal_mutexid drmt_mem_mutex;

#define DRM_MEM_MUTEX_BEGIN        kal_take_mutex(drmt_mem_mutex)
#define DRM_MEM_MUTEX_END       kal_give_mutex(drmt_mem_mutex)


/*****************************************************************************
 * FUNCTION
 *  drmt_mem_init
 * DESCRIPTION
 *  Allocates memory blocks.
 * PARAMETERS
 *  void
 *  size(?)     [IN]        The size of the (continuous) memory block to allocate.(?)(?)(?)
 * RETURNS
 *  void
 *****************************************************************************/
void drmt_mem_init(void)
{
    /*----------------------------------------------------------------*/
    /* Local Variables                                                */
    /*----------------------------------------------------------------*/

    /*----------------------------------------------------------------*/
    /* Code Body                                                      */
    /*----------------------------------------------------------------*/
    DRM_MEM_DEBUG((TRACE_GROUP_3, "[DRM] mem_init"));

    drmt_mem_mutex = kal_create_mutex("DRM MEM");

    memset(drmt_mem_pool, 0, DRM_MEM_SIZE);

#ifdef DRM_MEM_LOG_ON
    drmt_mem_id = kal_adm_create(drmt_mem_pool, DRM_MEM_SIZE, NULL, KAL_TRUE);
#else
    drmt_mem_id = kal_adm_create(drmt_mem_pool, DRM_MEM_SIZE, NULL, KAL_FALSE);
#endif

    ASSERT(drmt_mem_id);
}


/*****************************************************************************
 * FUNCTION
 *  drmt_mem_reinit
 * DESCRIPTION
 *  Allocates memory blocks.
 * PARAMETERS
 *  void
 *  size(?)     [IN]        The size of the (continuous) memory block to allocate.(?)(?)(?)
 * RETURNS
 *  void
 *****************************************************************************/
void drmt_mem_reinit(void)
{
    /*----------------------------------------------------------------*/
    /* Local Variables                                                */
    /*----------------------------------------------------------------*/

    /*----------------------------------------------------------------*/
    /* Code Body                                                      */
    /*----------------------------------------------------------------*/
    DRM_MEM_DEBUG((TRACE_GROUP_3, "[DRM] mem_reinit"));

    memset(drmt_mem_pool, 0, DRM_MEM_SIZE);

#ifdef DRM_MEM_LOG_ON
    drmt_mem_id = kal_adm_create(drmt_mem_pool, DRM_MEM_SIZE, NULL, KAL_TRUE);
#else
    drmt_mem_id = kal_adm_create(drmt_mem_pool, DRM_MEM_SIZE, NULL, KAL_FALSE);
#endif

    ASSERT(drmt_mem_id);
}


/*****************************************************************************
 * FUNCTION
 *  drmt_mem_total_left_size
 * DESCRIPTION
 *
 * PARAMETERS
 *  void
 * RETURNS
 *
 *****************************************************************************/
kal_uint32 drmt_mem_total_left_size(void)
{
    /*----------------------------------------------------------------*/
    /* Local Variables                                                */
    /*----------------------------------------------------------------*/
    kal_uint32 leftSize = 0;

    /*----------------------------------------------------------------*/
    /* Code Body                                                      */
    /*----------------------------------------------------------------*/
    /* DRM_MEM_DEBUG((TRACE_GROUP_3, "[DRM] mem_total_left_size")); */

    if (drmt_mem_id != 0)
    {
        leftSize = kal_adm_get_total_left_size(drmt_mem_id);
    }

    return leftSize;
}


/*****************************************************************************
 * FUNCTION
 *  drmt_mem_check_corruption
 * DESCRIPTION
 *  Allocates memory blocks.
 * PARAMETERS
 *  void
 *  size(?)     [IN]        The size of the (continuous) memory block to allocate.
 * RETURNS
 *  void
 *****************************************************************************/
void drmt_mem_check_corruption(void)
{
#ifdef DRM_MEM_CHECK_ON
    /*----------------------------------------------------------------*/
    /* Local Variables                                                */
    /*----------------------------------------------------------------*/
    void *corrupted_mem_address = NULL;

    /*----------------------------------------------------------------*/
    /* Code Body                                                      */
    /*----------------------------------------------------------------*/
    DRM_MEM_MUTEX_BEGIN;

    if (drmt_mem_id != 0)
    {
        corrupted_mem_address = kal_adm_check_integrity(drmt_mem_id);
        if (corrupted_mem_address != NULL)
        {
            kal_adm_print_log(drmt_mem_id);
            EXT_ASSERT(0, (int)corrupted_mem_address, (int)drmt_mem_total_left_size(), 0);
        }
    }
    DRM_MEM_MUTEX_END;
#endif /* DRM_MEM_DEBUG_ON */
}


/*****************************************************************************
 * FUNCTION
 *  drmt_mem_alloc_ext
 * DESCRIPTION
 *  Allocates memory blocks.
 * PARAMETERS
 *  size        [IN]        The size of the (continuous) memory block to allocate.
 *  file        [?]
 *  line        [IN]
 * RETURNS
 *  void
 *****************************************************************************/
void *drmt_mem_alloc_ext(int size, char *file, int line)
{
    /*----------------------------------------------------------------*/
    /* Local Variables                                                */
    /*----------------------------------------------------------------*/
    void *ptr = NULL;

#ifdef DRM_MEM_LOG_ON
    static int min_left_size = DRM_MEM_SIZE;
#endif

    /*----------------------------------------------------------------*/
    /* Code Body                                                      */
    /*----------------------------------------------------------------*/
    if (!size)
        size = 8;

    ASSERT(drmt_mem_id);

    drmt_mem_check_corruption();

    DRM_MEM_MUTEX_BEGIN;
    if (drmt_mem_id)
    {

    #ifdef DRM_MEM_LOG_ON
        ptr = (kal_uint32*) kal_adm_internal_alloc(drmt_mem_id, size, basename(file), line);
    #else
        ptr = (kal_uint32*) kal_adm_alloc(drmt_mem_id, size);
    #endif

        DRM_MEM_DEBUG((TRACE_GROUP_3, "[DRM] mem_alloc, file: %s, %d, block:%x size:%d", basename(file), line, ptr,
                       size));

        if (ptr == NULL)
        {
            if (drmt_mem_total_left_size() > size)
            {
                kal_bool FRAGMENTATION = KAL_FALSE;

                kal_adm_print_log(drmt_mem_id);
                ASSERT(FRAGMENTATION);  /* fragmentation */
            }
            else
            {
                kal_bool OUT_OF_MEMORY = KAL_FALSE;
                kal_adm_print_log(drmt_mem_id);
                ASSERT(OUT_OF_MEMORY);  /* out of memory */
            }
        }
        else
        {
        #ifdef DRM_MEM_LOG_ON
            size = drmt_mem_total_left_size();
            if (size < min_left_size)
            {
                min_left_size = size;
                drm_trace(TRACE_GROUP_3, "[DRM] mem_alloc, leftsize:%d", min_left_size);
            }
        #endif
        }
    }
    DRM_MEM_MUTEX_END;

    return ptr;
}


/*****************************************************************************
 * FUNCTION
 *  drmt_mem_realloc_ext
 * DESCRIPTION
 *  Reallocate memory blocks.
 * PARAMETERS
 *  memblock        [?]         [?]         [?]         [?]         [?]         [In] The block to reallocate.
 *  size            [IN]        [In] The size of the (continuous) memory block to allocate.
 *  file            [?]
 *  line            [IN]
 * RETURNS
 *  non-NULL: pointer to reallocated space.
 *  NULL: memory reallocation failed (original block is left
 *  unchanged) or size was not positive (the block is freed).(?)(?)(?)(?)(?)
 *****************************************************************************/
void *drmt_mem_realloc_ext(void *memblock, int size, char *file, int line)
{
    /*----------------------------------------------------------------*/
    /* Local Variables                                                */
    /*----------------------------------------------------------------*/

    /*----------------------------------------------------------------*/
    /* Code Body                                                      */
    /*----------------------------------------------------------------*/
    DRM_MEM_DEBUG((TRACE_GROUP_3, "[DRM] mem_realloc, file:%s %d block:%x size:%d", basename(file), line, memblock,
                   size));

    if (memblock)
    {
        void *ptr = NULL;

        if (size && (ptr = drmt_mem_alloc(size)) != NULL)
        {
            /* it is not safe here */
            memcpy(ptr, memblock, size);
        }

        drmt_mem_free(memblock);

        return ptr;
    }
    else
    {
        return drmt_mem_alloc(size);
    }
}


/*****************************************************************************
 * FUNCTION
 *  drmt_mem_free_ext
 * DESCRIPTION
 *  Deallocates or frees a memory block.
 * PARAMETERS
 *  memblock        [?]         [?]     [?]     [?]     [?]     [In] The memory block to free.
 *  file            [?]
 *  line            [IN]
 * RETURNS
 *  void
 *****************************************************************************/
void drmt_mem_free_ext(void *memblock, char *file, int line)
{
    /*----------------------------------------------------------------*/
    /* Local Variables                                                */
    /*----------------------------------------------------------------*/

    /*----------------------------------------------------------------*/
    /* Code Body                                                      */
    /*----------------------------------------------------------------*/
    DRM_MEM_DEBUG((TRACE_GROUP_3, "[DRM] mem_free, file: %s %d block:%x", basename(file), line, memblock));

    if (!memblock)
    {
        return;
    }

    drmt_mem_check_corruption();

    DRM_MEM_MUTEX_BEGIN;
    if (memblock)
    {
        kal_adm_free(drmt_mem_id, memblock);
    }
    DRM_MEM_MUTEX_END;
}


/*****************************************************************************
 * FUNCTION
 *  drmt_mem_delete
 * DESCRIPTION
 *  Deallocates or frees a memory block.
 * PARAMETERS
 *  void
 *  memblock        [?]     [?]     [?]     [?]     [In] The memory block to free.(?)
 * RETURNS
 *  void
 *****************************************************************************/
void drmt_mem_delete(void)
{
    /*----------------------------------------------------------------*/
    /* Local Variables                                                */
    /*----------------------------------------------------------------*/
    kal_status ret;

    /*----------------------------------------------------------------*/
    /* Code Body                                                      */
    /*----------------------------------------------------------------*/
    DRM_MEM_DEBUG((TRACE_GROUP_3, "[DRM] mem_delete"));

    DRM_MEM_MUTEX_BEGIN;
    if (drmt_mem_id)
    {
        ret = kal_adm_delete(drmt_mem_id);
        drmt_mem_id = 0;
        ASSERT(ret == KAL_SUCCESS);
    }
    DRM_MEM_MUTEX_END;
}


/*****************************************************************************
 * FUNCTION
 *  drmt_mem_valid
 * DESCRIPTION
 * PARAMETERS
 *  address     [IN]
 * RETURNS
 *****************************************************************************/
kal_bool drmt_mem_valid(kal_uint32 address)
{
    /*----------------------------------------------------------------*/
    /* Local Variables                                                */
    /*----------------------------------------------------------------*/
    kal_uint32 start = (kal_uint32) & drmt_mem_pool[0];

    /*----------------------------------------------------------------*/
    /* Code Body                                                      */
    /*----------------------------------------------------------------*/
    if (address >= start && address <= start + sizeof(drmt_mem_pool))
    {
        DRM_MEM_DEBUG((TRACE_GROUP_3, "[DRM] mem valid, add:%x", address));
        return KAL_TRUE;
    }
    else
    {
        DRM_MEM_DEBUG((TRACE_GROUP_3, "[DRM] mem invalid, add:%x", address));
        return KAL_FALSE;
    }
}
#endif /* __DRM_V02__ */
#endif /* __DRM_SUPPORT__ */

